package it.unibo.refolding.pico.stripchart;

import it.unibo.refolding.common.CommonThings;
import it.unibo.refolding.common.DoublePair;
import it.unibo.refolding.pico.ApproachRetractionLoop;
import it.unibo.refolding.pico.PlainPicoData;
import it.unibo.refolding.pico.StripChartPicoData;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntLists;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Charsets;

public class JpHelper {

	private static final Logger logger = Logger.getLogger(FetchRefoldBreak.class.getSimpleName());

	public static void writeToFile(IntList junctionPoints, Path junctionPointsFile)
			throws IOException {
    Files.write(junctionPointsFile, (toRepr(junctionPoints) + "\n").getBytes(Charsets.ISO_8859_1));
	}

  public static String toRepr(IntList junctionPoints) {
    return StringUtils.join(junctionPoints, ", ");
  }
  
  private static final Pattern jpSeparator = Pattern.compile(",\\s*");

	public static IntList extractJunctionPoints(Path junctionPointsFile) throws IOException {
		final int[] junctionPoints;
		String s = new String(Files.readAllBytes(junctionPointsFile), Charsets.ISO_8859_1);
		String trimmed = s.trim();
    String[] split = trimmed.isEmpty() ? new String[0] : jpSeparator.split(trimmed);
		junctionPoints = new int[split.length];
		for (int i = split.length; --i >= 0;) {
			junctionPoints[i] = Integer.parseInt(split[i]);
		}
		return IntLists.unmodifiable(IntArrayList.wrap(junctionPoints));
	}

	public static IntList computeJunctionPoints(FetchRefoldBreakParams params,
			StripChartPicoData stripChart, DoubleList allowedRelativeTimeError) {
		final IntList junctionPoints;
		IntList jp = StripChartRefolding.findJunctionPoints(stripChart, params, allowedRelativeTimeError);
		if (jp == null) {
			logger.warning("Assigning default junction points.");
			junctionPoints = createDefaultJunctionPoints(StripChartRefolding.FETCH_REFOLD_BREAK_JUNCTIONS);
		} else {
			junctionPoints = jp;
		}
		return junctionPoints;
	}

  public static IntList createDefaultJunctionPoints(int n) {
    int[] junctionPoints = new int[n];
    for (int i = junctionPoints.length; --i >= 0;) {
    	junctionPoints[i] = i;
    }
    return IntLists.unmodifiable(IntArrayList.wrap(junctionPoints));
  }
	
	public static boolean areJunctionPointsDefault(IntList jp) {
	  int i = 0;
	  for (; i < jp.size() && jp.getInt(i) == i; ++i) {}
	  return i == jp.size();
	}

	public static Option createJunctionPointsOption() {
		Option junctionPointsDirOption = new Option("junctionPointsDir", true,
				"junctionPointsDirectory with files");
		junctionPointsDirOption.setRequired(false);
		junctionPointsDirOption.setType(String.class);
		return junctionPointsDirOption;
	}

	public static IntList readOrComputeAndWriteJunctionPoints(FetchRefoldBreakParams params,
			StripChartPicoData stripChart, DoubleList allowedRelativeTimeError, Path junctionPointsFile) throws IOException {
		final IntList junctionPoints;
		if (Files.isRegularFile(junctionPointsFile)) {
			junctionPoints = extractJunctionPoints(junctionPointsFile);
		} else {
			if (params == null) {
				logger.log(Level.WARNING, "params is null and {0} does not exist", junctionPointsFile);
				junctionPoints = null;
			} else {
				junctionPoints = computeJunctionPoints(params, stripChart, allowedRelativeTimeError);
				writeToFile(junctionPoints, junctionPointsFile);
			}
		}
		return junctionPoints;
	}

	public static Path junctionPointsFile(Path junctionPointsDir, Path file) {
		return junctionPointsDir == null ? null : junctionPointsDir.resolve(file.getFileName() + ".junctionPoints");
	}

	public static DoublePair hoverZDef(IntList a, StripChartPicoData stripChart) {
		double hoverPointZ = CommonThings.mean(stripChart.sharedApproach().sharedZArray(), a.getInt(5),
				a.getInt(6) + 1);
		double hoverPointDef = CommonThings.mean(stripChart.sharedApproach()
				.sharedDeflectionArray(), a.getInt(5), a.getInt(6) + 1);
		DoublePair hoverZDef = DoublePair.of(hoverPointZ, hoverPointDef);
		return hoverZDef;
	}

	public static Path createJpDirIfNeeded(Option junctionPointsDirOption, CommandLine line) {
		String junctionPointsDir = line.getOptionValue(junctionPointsDirOption.getOpt());
		Path junctionPointsDirectory = StringUtils.isEmpty(junctionPointsDir) ? null : Paths.get(
				junctionPointsDir);
		if (junctionPointsDirectory != null) {
			CommonThings.createDirectoryPathIfNeeded(junctionPointsDirectory);
		}
		return junctionPointsDirectory;
	}

	/**
	 * @see refolding_full.read_waiting_time
	 */
	public static double readWaitingTimeS(IntList jp) {
		return (jp.getInt(6) - jp.getInt(5)) / 5000.0;
	}

  public static IntList junctionPoints(List<PlainPicoData> a) {
    int k = ApproachRetractionLoop.APPROACH_RETRACTION_SEGMENTS + 1;
    int[] jp = new int[a.size() * k];
    for (int i = 0; i < a.size(); ++i) {
      PlainPicoData plain = a.get(i);
      jp[i * k] = (i == 0 ? 0 : jp[i * k - 1] + 1);
      jp[i * k + 1] = jp[i * k] + plain.sharedApproach().timeInstantCount() - 1;
      jp[i * k + 2] = jp[i * k + 1] + 1;
      jp[i * k + 3] = jp[i * k + 2] + plain.sharedRetraction().timeInstantCount() - 1;
    }
    return IntLists.unmodifiable(IntArrayList.wrap(jp));
  }
}
